#include "OGRFileFormat.h"

/**
 *
 **/
OGRFileFormat::OGRFileFormat(PFSInfo info) : AbstractFileFormat(info) {
	this->ogrWriteMode = false;
	this->firstWrite = false;
	this->isMainThread = false;
	info->sizeParam = this->sizeParam;
	this->memory = new MemoryFileFormat(info);
    /* remove first slash '/' AND File extension */
    this->dstLayerName = info->path.substr(1, info->path.size() - 5);

    /* replace '/' with '_' */
    boost::algorithm::replace_all(this->dstLayerName, PATH_SEPERATOR, "_");

    this->srcLayerName = info->table;

    PDatabase db = info->filesystem->getDBConnection();
    this->srcDriverName = db->getDBDriverName();

    if(this->srcDriverName.compare("MYSQL") == 0)
    {
        this->srcFile = "MYSQL:"+info->database+",user="+db->getUserName()+",password="+db->getPassword()+",port="+db->getPort()+",host="+db->getHost();
    }
    else if(this->srcDriverName.compare("PostgreSQL") == 0)
    {
        this->srcFile = "PG:dbname="+info->database+" active_schema="+info->schema+" host="+db->getHost()+" port="+db->getPort()+" user="+db->getUserName()+" password="+db->getPassword();
        Filesystem::debug(this->srcFile);
    }
    else if(this->srcDriverName.compare("OCI") == 0)
    {
        this->srcFile = "OCI:"+db->getUserName()+"/"+db->getPassword()+"@"+db->getHost()+":"+db->getPort();
    }

    this->dstFile = info->filesystem->getMountDir() + info->filesystem->getCacheDirName()+PATH_SEPERATOR;
}

/**
 * Destructor, free memory
 **/
OGRFileFormat::~OGRFileFormat() {
    delete this->memory;
}

/**
 *
 **/
int OGRFileFormat::read(PFSInfo info) {
	if(this->loadDataFromDatabase(info) < 0) return -1;
    return this->memory->read(info);
}

/**
 *
 **/
int OGRFileFormat::write(PFSInfo info) {
	if(this->loadDataFromDatabase(info) < 0) return -1;
	PFileOperationRegistry operation = info->filesystem->getFileOperationRegistry(info->database, info->schema, info->table);
	if(operation == NULL) {
		info->filesystem->addFileOperationRegistry(info->database, info->schema, info->table);
		operation = info->filesystem->getFileOperationRegistry(info->database, info->schema, info->table);
		this->isMainThread = true;
		operation->isChanged = true;
	}

	if(operation != NULL && operation->isChanged == false)
	{
			this->isMainThread = true;
	}

	operation->isChanged = true;
	return this->memory->write(info);
}

/**
 *
 **/
int OGRFileFormat::close(PFSInfo info) {
    this->memory->close(info);
	if(this->isMainThread == true)
	{
		PFileOperationRegistry search = info->filesystem->getFileOperationRegistry(info->database, info->schema, info->table);
		if( search != NULL && search->hasWorkerThread == false)
        {
			search->hasWorkerThread = true;
			search->worker = new boost::thread(OGRFileFormat::run,  *info, this->srcDriverName, this->dstDriverName, this->srcFile, this->mainExtension);
		}
	}
    return 0;
}

int OGRFileFormat::loadDataFromDatabase(PFSInfo info)
{
	if(info->level == 4)
	{
		PFileOperationRegistry operation = info->filesystem->getFileOperationRegistry(info->database, info->schema, info->table);
		if(operation == NULL)
		{
			info->filesystem->addFileOperationRegistry(info->database, info->schema, info->table);
			String comparePath = info->filesystem->makeCacheFileName(info);
			comparePath = comparePath.substr(0, comparePath.size()-1-this->mainExtension.size());
			StringVector removeArray;
			for (MemoryFileMap::iterator it=info->filesystem->getMemoryFileMap()->begin() ; it != info->filesystem->getMemoryFileMap()->end(); it++ )
			{
				String file1 = it->first.substr(0, it->first.size()-mainExtension.size()-1);
				if(file1.compare(comparePath) == 0)
				{
					try{
						removeArray.push_back(it->first);
					}catch(...) {
					}
				}
			}

			for(StringVector::iterator vecIter=removeArray.begin() ; vecIter < removeArray.end(); vecIter++ )
			{
				PMemoryFile file = info->filesystem->getMemoryFile(*vecIter, true);
				free(file->fileContent);
				info->filesystem->getMemoryFileMap()->erase(*vecIter);
			}
            int err = OGRFileFormat::convert(&this->dstDriverName, &this->srcDriverName, &this->dstFile, &this->srcFile, &this->dstLayerName, &this->srcLayerName, NULL, true);
			operation = info->filesystem->getFileOperationRegistry(info->database, info->schema, info->table);
			operation->created = true;
			operation->createMode = false;
			this->isMainThread = true;
			if(err < 0) return err;
		}
		else if(operation->created == false) {
			while(operation->createMode == false) {
				MY_SLEEP(100);
			}
		}
	}
	return 0;
}

/**
 *
 **/
void OGRFileFormat::wait(PFilesystem filesystem, String &path, String &mainExtension, unsigned int sleeptime)
{
	bool run = true;
	bool runAgain = false;
	while(run)
	{
		MY_SLEEP(sleeptime);
		runAgain = false;
		String file2 = path.substr(0, path.size() - mainExtension.size()-1);
		for (MemoryFileMap::iterator it = filesystem->getMemoryFileMap()->begin() ; it != filesystem->getMemoryFileMap()->end(); it++ )
		{
			String file1 = it->first.substr(0, it->first.size() - mainExtension.size()-1);
			if(file1.compare(file2) == 0 && (it->second.write || it->second.read))
			{
				runAgain = true;
			}
		}
		run = runAgain;
	}
}


void OGRFileFormat::run(FSInfo info, String dstDriverName, String srcDriverName, String dstFile, String mainExtension)
{
	PFileOperationRegistry operation = info.filesystem->getFileOperationRegistry(info.database, info.schema, info.table);
	PFilesystem filesystem = info.filesystem;
	info.path = filesystem->makeCacheFileName(&info);
	String search = info.path;
	OGRFileFormat::wait(filesystem, info.path, mainExtension, 1000);
	if(operation->isChanged == true)
	{
		OGRFileFormat::wait(filesystem, info.path, mainExtension, 500);
		String srcFile = filesystem->getMountDir()+info.path.substr(1, info.path.size()-mainExtension.size()-1)+mainExtension;

		StringVector pathArray = Filesystem::getPathElements(info.path);
		String srcLayerName = pathArray.at(pathArray.size()-1);
		srcLayerName = srcLayerName.substr(0, srcLayerName.size()-mainExtension.size()-1);
		String dstLayerName = info.table;

		char **options = NULL;

		if(dstDriverName.compare("PostgreSQL") == 0 || dstDriverName.compare("OCI"))
			options = CSLSetNameValue( options, "GEOMETRY_NAME", filesystem->getGeomFieldName().c_str() );
		else if(dstDriverName.compare("MYSQL") == 0)
			options = CSLSetNameValue( options, "MYSQL_GEOM_COLUMN", filesystem->getGeomFieldName().c_str() );

		OGRFileFormat::convert(&dstDriverName, &srcDriverName, &dstFile, &srcFile, &dstLayerName, &srcLayerName, options, false);
		search = srcFile;
	}
	else
	{
		OGRFileFormat::wait(info.filesystem, info.path, mainExtension, info.filesystem->getCacheLifeTime());
	}

	Filesystem::debug("clean cache start");
	StringVector removeArray;
	String cacheFileName = search.substr(0, search.size()-mainExtension.size()-1);
    for (MemoryFileMap::iterator it=filesystem->getMemoryFileMap()->begin() ; it != filesystem->getMemoryFileMap()->end(); it++ )
    {
        String file1 = it->first.substr(0, it->first.size()-mainExtension.size()-1);
        if(file1.compare(cacheFileName) == 0)
        {
            try{
                removeArray.push_back(it->first);
            }catch(...) {
            }
        }
    }

    for(StringVector::iterator vecIter=removeArray.begin() ; vecIter < removeArray.end(); vecIter++ )
	{
        PMemoryFile file = filesystem->getMemoryFile(*vecIter, true);
        free(file->fileContent);
        filesystem->getMemoryFileMap()->erase(*vecIter);
    }
    filesystem->removeFileOperationRegistry(info.database, info.schema, info.table);
	Filesystem::debug("clean cache end");
}

/**
 *
 **/
int OGRFileFormat::convert(PString dstDriverName, PString srcDriverName, PString dstFile, PString srcFile, PString dstLayerName, PString srcLayerName, char *options[], bool isRead)
{
    std::cout << "please wait saving..." << std::endl;
	options = CSLSetNameValue( options, "OVERWRITE", "YES" );
	Filesystem::debug("[begin]\n"
	"dstDriverName: " + *dstDriverName + "\n"
    "srcDriverName: " + *srcDriverName + "\n"
    "dstFile:       " + *dstFile + "\n"
    "srcFile:       " + *srcFile + "\n"
    "dstLayerName:  " + *dstLayerName + "\n"
    "srcLayerName:  " + *srcLayerName  + "\n\n");

    OGRSFDriver *dstDriver;
    OGRSFDriver *srcDriver;

    OGRLayer  *dstLayer;
    OGRLayer  *srcLayer;

    OGRDataSource *dstDataSource;
    OGRDataSource *srcDataSource;

    OGRFeature *srcFeature;

    OGRRegisterAll();

    dstDriver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName( dstDriverName->c_str() );
    srcDriver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName( srcDriverName->c_str() );

    if( dstDriver == NULL ) {
		std::cout << "[gdal] " << *dstDriverName << " driver not available." << std::endl;
		return -1;
    }

    if( srcDriver == NULL ) {
		std::cout << "[gdal] " << *srcDriverName << " driver not available." << std::endl;
        return -1;
    }

    dstDataSource = dstDriver->CreateDataSource( dstFile->c_str(), NULL );

    if( dstDataSource == NULL ) {
		std::cout << "[gdal] error dstDataSource." << std::endl;
		std::cout << "[gdal] destination file: " << *dstFile << std::endl;
		return -1;
    }

    srcDataSource = OGRSFDriverRegistrar::Open( srcFile->c_str(), 0, &srcDriver);

    if( srcDataSource == NULL ) {
        std::cout << "[gdal] error srcDataSource" << std::endl;
		std::cout << "[gdal] source file: " << *srcFile << std::endl;
		return -1;
    }

    if(isRead) {
        srcLayer = srcDataSource->GetLayerByName(srcLayerName->c_str());
    } else {
        srcLayer = srcDataSource->GetLayer(0);
    }

    if(srcLayer == NULL) {
		std::cout << "[gdal] error source layer: is not valid" << std::endl;
		std::cout << "[gdal] Layername: " << *srcLayerName << std::endl;
        return -1;
    }

    srcLayer->ResetReading();

    OGRFeatureDefn *featureDefn = srcLayer->GetLayerDefn();

    dstLayer = dstDataSource->CreateLayer( dstLayerName->c_str(), srcLayer->GetSpatialRef(), featureDefn->GetGeomType(), options );
    if(dstLayer == NULL) {
		std::cout << "[gdal] error destination layer: is not valid" << std::endl;
		std::cout << "[gdal] Layername: " << *dstLayerName << std::endl;
        return -1;
    }

    for(int i = 0; i < featureDefn->GetFieldCount(); i++) {
        dstLayer->CreateField(featureDefn->GetFieldDefn(i),i);
    }

    while( (srcFeature = srcLayer->GetNextFeature()) != NULL ) {
		srcFeature->SetFID(-1);
		if( dstLayer->CreateFeature( srcFeature ) != OGRERR_NONE )
        {
			std::cout << "[gdal] Failed to create feature in shapefile." << std::endl;
			return -1;
        }
        OGRFeature::DestroyFeature( srcFeature );
    }

    OGRDataSource::DestroyDataSource( srcDataSource );
    OGRDataSource::DestroyDataSource( dstDataSource );
	//CSLDestroy(options);
	Filesystem::debug("[end]\n");
	std::cout << "saving complete" << std::endl;
    return 0;
}
